﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Data.SqlTypes;
using Djs.Neuronic.Brain;
using Djs.Neuronic.Core;

namespace Djs.Neuronic.Medium
{
    #region class DbElementDatabase : informace o databázi
    /// <summary>
    /// Data o databázi
    /// </summary>
    public class DbElementDatabase : DbElement
    {
        #region Konstrukce, proměnné, tvorba z XML deklarace
        public DbElementDatabase()
        {
            this.SetDefaults();
        }
        public static DbElementDatabase CreateFromXml(XElement element)
        {
            if (element == null) return null;
            if (element.Name.LocalName != "db_properties") return null;
            DbElementDatabase database = new DbElementDatabase();
            database.Name = element.GetAttributeString("Name");
            database.CompatibilityLevel = XmlToProperty(element, "CompatibilityLevel", database.CompatibilityLevel);
            database.Collation = element.GetAttributeString("Collation");
            database.DataSizeKb = element.GetAttributeInt32N("DataSizeKb");
            database.DataGrowthKb = element.GetAttributeInt32N("DataGrowthKb");
            database.LogSizeKb = element.GetAttributeInt32N("LogSizeKb");
            database.LogGrowthPercent = element.GetAttributeInt32N("LogGrowthPercent");
            database.IsAnsiNullDefault = XmlToProperty(element, "AnsiNullDefault", database.IsAnsiNullDefault);
            database.IsAnsiNulls = XmlToProperty(element, "AnsiNulls", database.IsAnsiNulls);
            database.IsAnsiPadding = XmlToProperty(element, "AnsiPadding", database.IsAnsiPadding);
            database.IsAnsiWarnings = XmlToProperty(element, "AnsiWarnings", database.IsAnsiWarnings);
            database.IsArithAbort = XmlToProperty(element, "ArithAbort", database.IsArithAbort);
            database.IsAutoClose = XmlToProperty(element, "AutoClose", database.IsAutoClose);
            database.IsAutoCreateStats = XmlToProperty(element, "AutoCreateStats", database.IsAutoCreateStats);
            database.IsAutoShrink = XmlToProperty(element, "AutoShrink", database.IsAutoShrink);
            database.IsAutoUpdateStats = XmlToProperty(element, "AutoUpdateStats", database.IsAutoUpdateStats);
            database.IsAutoUpdateStatsAsync = XmlToProperty(element, "AutoUpdateStatsAsync", database.IsAutoUpdateStatsAsync);
            database.IsCursorCloseOnCommit = XmlToProperty(element, "CursorCloseOnCommit", database.IsCursorCloseOnCommit);
            database.CursorDefault = XmlToProperty(element, "CursorDefault", database.CursorDefault);
            database.IsConcatNullYieldsNull = XmlToProperty(element, "ConcatNullYieldsNull", database.IsConcatNullYieldsNull);
            database.IsNumericRoundabort = XmlToProperty(element, "NumericRoundabort", database.IsNumericRoundabort);
            database.IsQuotedIdentifier = XmlToProperty(element, "QuotedIdentifier", database.IsQuotedIdentifier);
            database.IsRecursiveTriggers = XmlToProperty(element, "RecursiveTriggers", database.IsRecursiveTriggers);
            database.BrokerMode = XmlToProperty(element, "BrokerMode", database.BrokerMode);
            database.DateCorrelationOptimization = XmlToProperty(element, "DateCorrelation", database.DateCorrelationOptimization);
            database.Parameterization = XmlToProperty(element, "Parameterization", database.Parameterization);
            database.ReadWrite = XmlToProperty(element, "ReadOnly", database.ReadWrite);
            database.RecoveryModel = XmlToProperty(element, "RecoveryModel", database.RecoveryModel);
            database.UserAccess = XmlToProperty(element, "UserAccess", database.UserAccess);
            database.PageVerifyOption = XmlToProperty(element, "PageVerifyOption", database.PageVerifyOption);
            database.DbFileMain = null;
            database.SizeKB = 0;
            return database;
        }
        protected void SetDefaults()
        {
            this.Collation = "SQL_Czech_CP1250_CI_AS";
            this.CompatibilityLevel = CompatibilityLevelType.Sql100;
            this.IsAnsiNullDefault = false;
            this.IsAnsiNulls = false;
            this.IsAnsiPadding = false;
            this.IsAnsiWarnings = false;
            this.IsArithAbort = false;
            this.IsAutoClose = true;
            this.IsAutoCreateStats = true;
            this.IsAutoShrink = false;
            this.IsAutoUpdateStats = true;
            this.IsAutoUpdateStatsAsync = false;
            this.IsCursorCloseOnCommit = false;
            this.CursorDefault = CursorDefaultType.Global;
            this.IsConcatNullYieldsNull = false;
            this.IsNumericRoundabort = false;
            this.IsQuotedIdentifier = false;
            this.IsRecursiveTriggers = false;
            this.BrokerMode = BrokerType.Disabled;
            this.DateCorrelationOptimization = false;
            this.Parameterization = ParametrizationType.Simple;
            this.ReadWrite = ReadWriteType.ReadWrite;
            this.RecoveryModel = RecoveryType.Simple;
            this.UserAccess = UserAccesType.MultiUser;
            this.PageVerifyOption = PageVerifyType.CheckSum;
        }
        #endregion
        #region Enums a konverze
        #region Enums
        public enum CompatibilityLevelType
        {
            None,
            /// <summary>
            /// 
            /// </summary>
            Sql80,
            /// <summary>
            /// 
            /// </summary>
            Sql90,
            Sql100
        }
        public enum CursorDefaultType
        {
            None,
            /// <summary>
            /// When LOCAL is specified and a cursor is not defined as GLOBAL when created, the scope of the cursor is local to the batch, stored procedure, or trigger in which the cursor was created. The cursor name is valid only within this scope.
            /// </summary>
            Local,
            /// <summary>
            /// When GLOBAL is specified, and a cursor is not defined as LOCAL when created, the scope of the cursor is global to the connection. The cursor name can be referenced in any stored procedure or batch executed by the connection.
            /// </summary>
            Global
        }
        public enum BrokerType
        {
            None,
            /// <summary>
            /// When ENABLE_BROKER is specified, Service Broker is enabled for the specified database.
            /// </summary>
            Enabled,
            /// <summary>
            /// When DISABLE_BROKER is specified, Service Broker is disabled for the specified database.
            /// </summary>
            Disabled,
            /// <summary>
            /// When NEW_BROKER is specified, the database receives a new broker identifier.
            /// </summary>
            NewBroker,
            /// <summary>
            /// When ERROR_BROKER_CONVERSATIONS is specified, conversations in the database receive an error message when the database is attached.
            /// </summary>
            ErrorBrokerConversation
        }
        public enum ParametrizationType
        {
            None,
            /// <summary>
            /// When SIMPLE specified, queries are parameterized based on the default behavior of the database.
            /// </summary>
            Simple,
            /// <summary>
            /// When FORCED is specified, SQL Server parameterizes all queries in the database.
            /// </summary>
            Forced
        }
        public enum ReadWriteType
        {
            None,
            ReadWrite,
            ReadOnly
        }
        public enum RecoveryType
        {
            None,
            /// <summary>
            /// When SIMPLE is specified, a simple backup strategy is provided that uses minimal log space.
            /// </summary>
            Simple,
            /// <summary>
            /// When BULK_LOGGED is specified, recovery after media failure by combining the best performance and least amount of log-space use for certain large-scale or bulk operations is provided.
            /// </summary>
            BulkLogged,
            /// <summary>
            /// When FULL is specified, full recovery after media failure is provided by using transaction log backups. If a data file is damaged, media recovery can restore all committed transactions.
            /// </summary>
            Full
        }
        public enum UserAccesType
        {
            None,
            /// <summary>
            /// When SINGLE_USER is specified, one user at a time is allowed to connect to the database. All other user connections are broken. 
            /// </summary>
            SingleUser,
            /// <summary>
            /// When RESTRICTED_USER is specified, only members of the db_owner fixed database role and dbcreator and sysadmin fixed server roles are allowed to connect to the database, but it does not limit their number.
            /// </summary>
            RestrictedUser,
            /// <summary>
            /// When MULTI_USER is specified, all users that have the appropriate permissions to connect to the database are allowed.
            /// </summary>
            MultiUser
        }
        public enum PageVerifyType
        {
            /// <summary>
            /// When NONE is specified, database page writes will not generate a CHECKSUM or TORN_PAGE_DETECTION value. SQL Server will not verify a checksum or torn page during a read even if a CHECKSUM or TORN_PAGE_DETECTION value is present in the page header.
            /// </summary>
            None,
            /// <summary>
            /// When CHECKSUM is specified, the Database Engine calculates a checksum over the contents of the whole page and stores the value in the page header when a page is written to disk. When the page is read from disk, the checksum is recomputed and compared to the checksum value stored in the page header.
            /// </summary>
            CheckSum,
            /// <summary>
            /// When TORN_PAGE_DETECTION is specified, a specific 2-bit pattern for each 512-byte sector in the 8-kilobyte (KB) database page is saved and stored in the database page header when the page is written to disk. When the page is read from disk, the torn bits stored in the page header are compared to the actual page sector information. 
            /// </summary>
            TornPageDetection
        }
        #endregion
        #region Konverze enumů, db hodnot, XML hodnot, db settingu
        private static CompatibilityLevelType DbValueToProperty(byte recovery, CompatibilityLevelType defaultValue)
        {
            switch (recovery)
            {
                case 80: return CompatibilityLevelType.Sql80;
                case 90: return CompatibilityLevelType.Sql90;
                case 100: return CompatibilityLevelType.Sql100;
            }
            return defaultValue;
        }
        private static CursorDefaultType DbValueToProperty(bool cursorDefault, CursorDefaultType defaultValue)
        {
            return (cursorDefault ? CursorDefaultType.Local : CursorDefaultType.Global);
        }
        private static BrokerType DbValueToProperty(bool broker, BrokerType defaultValue)
        {
            return (broker ? BrokerType.Enabled : BrokerType.Disabled);
        }
        private static ParametrizationType DbValueToProperty(bool parametrization, ParametrizationType defaultValue)
        {
            return (parametrization ? ParametrizationType.Forced : ParametrizationType.Simple);
        }
        private static ReadWriteType DbValueToProperty(bool isReadOnly, ReadWriteType defaultValue)
        {
            return (isReadOnly ? ReadWriteType.ReadOnly : ReadWriteType.ReadWrite);
        }
        private static RecoveryType DbValueToProperty(byte recovery, RecoveryType defaultValue)
        {
            switch (recovery)
            {
                case 0: return RecoveryType.None;
                case 1: return RecoveryType.Full;
                case 2: return RecoveryType.BulkLogged;
                case 3: return RecoveryType.Simple;
            }
            return defaultValue;
        }
        private static UserAccesType DbValueToProperty(byte userAccess, UserAccesType defaultValue)
        {
            switch (userAccess)
            {
                case 0: return UserAccesType.MultiUser;
                case 1: return UserAccesType.SingleUser;
                case 2: return UserAccesType.RestrictedUser;
            }
            return defaultValue;
        }
        private static PageVerifyType DbValueToProperty(byte pageVerify, PageVerifyType defaultValue)
        {
            switch (pageVerify)
            {
                case 0: return PageVerifyType.None;
                case 1: return PageVerifyType.TornPageDetection;
                case 2: return PageVerifyType.CheckSum;
            }
            return defaultValue;
        }
        private static CompatibilityLevelType XmlToProperty(XElement element, string attributeName, CompatibilityLevelType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "80": return CompatibilityLevelType.Sql80;
                    case "90": return CompatibilityLevelType.Sql90;
                    case "100": return CompatibilityLevelType.Sql100;
                }
            }
            return defaultValue;
        }
        private static CursorDefaultType XmlToProperty(XElement element, string attributeName, CursorDefaultType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "Local": return CursorDefaultType.Local;
                    case "Global": return CursorDefaultType.Global;
                }
            }
            return defaultValue;
        }
        private static BrokerType XmlToProperty(XElement element, string attributeName, BrokerType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "Disabled": return BrokerType.Disabled;
                    case "Enabled": return BrokerType.Enabled;
                    case "NewBroker": return BrokerType.NewBroker;
                    case "ErrorBrokerConversation": return BrokerType.ErrorBrokerConversation;
                }
            }
            return defaultValue;
        }
        private static ParametrizationType XmlToProperty(XElement element, string attributeName, ParametrizationType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "Simple": return ParametrizationType.Simple;
                    case "Forced": return ParametrizationType.Forced;
                }
            }
            return defaultValue;
        }
        private static ReadWriteType XmlToProperty(XElement element, string attributeName, ReadWriteType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "ReadWrite": return ReadWriteType.ReadWrite;
                    case "ReadOnly": return ReadWriteType.ReadOnly;
                }
            }
            return defaultValue;
        }
        private static RecoveryType XmlToProperty(XElement element, string attributeName, RecoveryType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "Simple": return RecoveryType.Simple;
                    case "BulkLogged": return RecoveryType.BulkLogged;
                    case "Full": return RecoveryType.Full;
                }
            }
            return defaultValue;
        }
        private static UserAccesType XmlToProperty(XElement element, string attributeName, UserAccesType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "SingleUser": return UserAccesType.SingleUser;
                    case "RestrictedUser": return UserAccesType.RestrictedUser;
                    case "MultiUser": return UserAccesType.MultiUser;
                }
            }
            return defaultValue;
        }
        private static PageVerifyType XmlToProperty(XElement element, string attributeName, PageVerifyType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value)
                {
                    case "None": return PageVerifyType.None;
                    case "CheckSum": return PageVerifyType.CheckSum;
                    case "TornPageDetection": return PageVerifyType.TornPageDetection;
                }
            }
            return defaultValue;
        }
        private static string PropertyToSqlCommand(CompatibilityLevelType value)
        {
            switch (value)
            {
                case CompatibilityLevelType.Sql80: return "80";
                case CompatibilityLevelType.Sql90: return "90";
                case CompatibilityLevelType.Sql100: return "100";
            }
            return "100";
        }
        private static string PropertyToSqlCommand(CursorDefaultType value)
        {
            switch (value)
            {
                case CursorDefaultType.Local: return "LOCAL";
                case CursorDefaultType.Global: return "GLOBAL";
            }
            return "GLOBAL";
        }
        private static string PropertyToSqlCommand(BrokerType value)
        {
            switch (value)
            {
                case BrokerType.Disabled: return "DISABLE_BROKER";
                case BrokerType.Enabled: return "ENABLE_BROKER";
                case BrokerType.NewBroker: return "NEW_BROKER";
                case BrokerType.ErrorBrokerConversation: return "ERROR_BROKER_CONVERSATIONS";
            }
            return "DISABLE_BROKER";
        }
        private static string PropertyToSqlCommand(ParametrizationType value)
        {
            switch (value)
            {
                case ParametrizationType.Simple: return "SIMPLE";
                case ParametrizationType.Forced: return "FORCED";
            }
            return "SIMPLE";
        }
        private static string PropertyToSqlCommand(ReadWriteType value)
        {
            switch (value)
            {
                case ReadWriteType.ReadWrite: return "READ_WRITE";
                case ReadWriteType.ReadOnly: return "READ_ONLY";
            }
            return "READ_WRITE";
        }
        private static string PropertyToSqlCommand(RecoveryType value)
        {
            switch (value)
            {
                case RecoveryType.Simple: return "SIMPLE";
                case RecoveryType.BulkLogged: return "BULK_LOGGED";
                case RecoveryType.Full: return "FULL";
            }
            return "SIMPLE";
        }
        private static string PropertyToSqlCommand(UserAccesType value)
        {
            switch (value)
            {
                case UserAccesType.SingleUser: return "SINGLE_USER";
                case UserAccesType.RestrictedUser: return "RESTRICTED_USER";
                case UserAccesType.MultiUser: return "MULTI_USER";
            }
            return "MULTI_USER";
        }
        private static string PropertyToSqlCommand(PageVerifyType value)
        {
            switch (value)
            {
                case PageVerifyType.None: return "NONE";
                case PageVerifyType.CheckSum: return "CHECKSUM";
                case PageVerifyType.TornPageDetection: return "TORN_PAGE_DETECTION";
            }
            return "CHECKSUM";
        }
        #endregion
        #endregion
        #region Property a Fill
        /// <summary>
        /// CompatibilityLevel
        /// </summary>
        public CompatibilityLevelType CompatibilityLevel { get; protected set; }
        /// <summary>
        /// Collation
        /// </summary>
        public string Collation { get; protected set; }
        /// <summary>
        /// Výchozí velikost datového souboru nové databáze v KB
        /// </summary>
        public int? DataSizeKb { get; protected set; }
        /// <summary>
        /// Nastavený přírůstek datového souboru databáze v KB
        /// </summary>
        public int? DataGrowthKb { get; protected set; }
        /// <summary>
        /// Výchozí velikost log souboru nové databáze v KB
        /// </summary>
        public int? LogSizeKb { get; protected set; }
        /// <summary>
        /// Nastavený přírůstek log souboru databáze v procentech
        /// </summary>
        public int? LogGrowthPercent { get; protected set; }
        /// <summary>
        /// Determines the default value, NULL or NOT NULL, of a column, alias data type, or CLR user-defined type for which the nullability is not explicitly defined in CREATE TABLE or ALTER TABLE statements. 
        /// When ON is specified, the default value is NULL.
        /// When OFF is specified, the default value is NOT NULL.
        /// </summary>
        public bool IsAnsiNullDefault { get; protected set; }
        /// <summary>
        /// When ON is specified, all comparisons to a null value evaluate to UNKNOWN.
        /// When OFF is specified, comparisons of non-UNICODE values to a null value evaluate to TRUE if both values are NULL.
        /// </summary>
        public bool IsAnsiNulls { get; protected set; }
        /// <summary>
        /// When set to ON, trailing blanks in character values inserted into varchar or nvarchar columns and trailing zeros in binary values inserted into varbinary columns are not trimmed. Values are not padded to the length of the column. 
        /// When set to OFF, the trailing blanks (for varchar or nvarchar) and zeros (for varbinary) are trimmed. This setting affects only the definition of new columns.
        /// char and binary columns that allow nulls are padded to the length of the column when ANSI_PADDING is set to ON, but trailing blanks and zeros are trimmed when ANSI_PADDING is OFF. char and binary columns that do not allow nulls are always padded to the length of the column. 
        /// </summary>
        public bool IsAnsiPadding { get; protected set; }
        /// <summary>
        /// When ON is specified, errors or warnings are issued when conditions such as divide-by-zero occur or null values appear in aggregate functions.
        /// When OFF is specified, no warnings are raised and null values are returned when conditions such as divide-by-zero occur.
        /// </summary>
        public bool IsAnsiWarnings { get; protected set; }
        /// <summary>
        /// When ON is specified, a query is ended when an overflow or divide-by-zero error occurs during query execution.
        /// When OFF is specified, a warning message is displayed when one of these errors occurs, but the query, batch, or transaction continues to process as if no error occurred. 
        /// </summary>
        public bool IsArithAbort { get; protected set; }
        /// <summary>
        /// When set to ON, the database is shut down cleanly and its resources are freed after the last user exits. The database automatically reopens when a user tries to use the database again.
        /// When set to OFF, the database remains open after the last user exits.
        /// </summary>
        public bool IsAutoClose { get; protected set; }
        /// <summary>
        /// When set to ON, statistics are automatically created on columns used in a predicate. 
        /// When set to OFF, statistics are not automatically created; instead, statistics can be manually created.
        /// </summary>
        public bool IsAutoCreateStats { get; protected set; }
        /// <summary>
        /// When set to ON, the database files are candidates for periodic shrinking. Both data file and log files can be shrunk automatically by SQL Server. AUTO_SHRINK reduces the size of the transaction log only if the database is set to SIMPLE recovery model or if the log is backed up. 
        /// When set to OFF, database files are not automatically shrunk during periodic checks for unused space.
        /// </summary>
        public bool IsAutoShrink { get; protected set; }
        /// <summary>
        /// When set to ON, any missing statistics required by a query for optimization are automatically built during query optimization. 
        /// When set to OFF, statistics must be manually created. For more information, see Using Statistics to Improve Query Performance.
        /// </summary>
        public bool IsAutoUpdateStats { get; protected set; }
        /// <summary>
        /// When set to ON, any cursors open when a transaction is committed or rolled back are closed.
        /// When set to OFF, remain open when a transaction is committed; rolling back a transaction closes any cursors except those defined as INSENSITIVE or STATIC. 
        /// </summary>
        public bool IsCursorCloseOnCommit { get; protected set; }
        /// <summary>
        /// When LOCAL is specified and a cursor is not defined as GLOBAL when created, the scope of the cursor is local to the batch, stored procedure, or trigger in which the cursor was created. The cursor name is valid only within this scope.
        /// When GLOBAL is specified, and a cursor is not defined as LOCAL when created, the scope of the cursor is global to the connection. The cursor name can be referenced in any stored procedure or batch executed by the connection.
        /// </summary>
        public CursorDefaultType CursorDefault { get; protected set; }
        /// <summary>
        /// When ON is specified, the result of a concatenation operation is NULL when either operand is NULL.
        /// When OFF is specified, the null value is treated as an empty character string.
        /// </summary>
        public bool IsConcatNullYieldsNull { get; protected set; }
        /// <summary>
        /// When ON is specified, an error is generated when loss of precision occurs in an expression.
        /// When OFF is specified, losses of precision do not generate error messages and the result is rounded to the precision of the column or variable storing the result. 
        /// </summary>
        public bool IsNumericRoundabort { get; protected set; }
        /// <summary>
        /// When ON is specified, double quotation marks can be used to enclose delimited identifiers. 
        /// When OFF is specified, identifiers cannot be in quotation marks and must follow all Transact-SQL rules for identifiers.
        /// </summary>
        public bool IsQuotedIdentifier { get; protected set; }
        /// <summary>
        /// When ON is specified, recursive firing of AFTER triggers is allowed.
        /// When OFF is specified, only direct recursive firing of AFTER triggers is not allowed. 
        /// </summary>
        public bool IsRecursiveTriggers { get; protected set; }
        /// <summary>
        /// Control Service Broker options. 
        /// <list type="">
        /// <item>
        /// When ENABLE_BROKER is specified, Service Broker is enabled for the specified database.
        /// </item>
        /// <item>
        /// When DISABLE_BROKER is specified, Service Broker is disabled for the specified database.
        /// </item>
        /// <item>
        /// When NEW_BROKER is specified, the database receives a new broker identifier. 
        /// </item>
        /// <item>
        /// When ERROR_BROKER_CONVERSATIONS is specified, conversations in the database receive an error message when the database is attached.
        /// </item>
        /// </list>
        /// </summary>
        public BrokerType BrokerMode { get; protected set; }
        /// <summary>
        /// When True, updates the statistics asynchronously.
        /// </summary>
        public bool IsAutoUpdateStatsAsync { get; protected set; }
        /// <summary>
        /// Control the date_correlation_optimization option.
        /// When ON is specified, SQL Server maintains correlation statistics between any two tables in the database that are linked by a FOREIGN KEY constraint and have datetime columns.
        /// When OFF is specified, correlation statistics are not maintained.
        /// </summary>
        public bool DateCorrelationOptimization { get; protected set; }
        /// <summary>
        /// Controls the parameterization option.
        /// When SIMPLE specified, queries are parameterized based on the default behavior of the database.
        /// When FORCED is specified, SQL Server parameterizes all queries in the database.
        /// </summary>
        public ParametrizationType Parameterization { get; protected set; }
        /// <summary>
        /// When READ_ONLY is specified, users can read data from the database but not modify it. 
        /// When READ_WRITE is specified, the database is available for read-and-write operations.
        /// </summary>
        public ReadWriteType ReadWrite { get; protected set; }
        /// <summary>
        /// Control the recovery model for the database.
        /// When FULL is specified, full recovery after media failure is provided by using transaction log backups. If a data file is damaged, media recovery can restore all committed transactions.
        /// When BULK_LOGGED is specified, recovery after media failure by combining the best performance and least amount of log-space use for certain large-scale or bulk operations is provided.
        /// When SIMPLE is specified, a simple backup strategy is provided that uses minimal log space.
        /// </summary>
        public RecoveryType RecoveryModel { get; protected set; }
        /// <summary>
        /// Control who can connect to the database.
        /// When SINGLE_USER is specified, one user at a time is allowed to connect to the database. All other user connections are broken. 
        /// When RESTRICTED_USER is specified, only members of the db_owner fixed database role and dbcreator and sysadmin fixed server roles are allowed to connect to the database, but it does not limit their number.
        /// When MULTI_USER is specified, all users that have the appropriate permissions to connect to the database are allowed.
        /// </summary>
        public UserAccesType UserAccess { get; protected set; }
        /// <summary>
        /// Control the recovery model for the database.
        /// When CHECKSUM is specified, the Database Engine calculates a checksum over the contents of the whole page and stores the value in the page header when a page is written to disk. When the page is read from disk, the checksum is recomputed and compared to the checksum value stored in the page header.
        /// When TORN_PAGE_DETECTION is specified, a specific 2-bit pattern for each 512-byte sector in the 8-kilobyte (KB) database page is saved and stored in the database page header when the page is written to disk. When the page is read from disk, the torn bits stored in the page header are compared to the actual page sector information. 
        /// When NONE is specified, database page writes will not generate a CHECKSUM or TORN_PAGE_DETECTION value. SQL Server will not verify a checksum or torn page during a read even if a CHECKSUM or TORN_PAGE_DETECTION value is present in the page header.
        /// </summary>
        public PageVerifyType PageVerifyOption { get; protected set; }
        /// <summary>
        /// sys.master_files.physical_name pro soubor obsahující ROWS této databáze (vlastní data, ne log ani fulltext)
        /// </summary>
        public string DbFileMain { get; protected set; }
        /// <summary>
        /// SizeKB: velikost všech souborů patřících k databázi v kilobytech (1024 Byte)
        /// </summary>
        public int SizeKB { get; protected set; }
        /// <summary>
        /// Tato tabulka má systémové jméno
        /// </summary>
        public bool IsSystemName { get { return IsSystemDatabaseName(this.Name); } }
        /// <summary>
        /// Tato tabulka má reportové jméno
        /// </summary>
        public bool IsReportName { get { return IsReportDatabaseName(this.Name); } }
        /// <summary>
        /// Tato tabulka má jméno uživatelské (ani systém, ani report)
        /// </summary>
        public bool IsUserName { get { return (!this.IsSystemName && !this.IsReportName); } }
        /// <summary>
        /// Načtení vlastních dat nad rámec předka DbElement
        /// </summary>
        /// <param name="data"></param>
        protected override void Fill(RecordFillInfo data)
        {
            this.CompatibilityLevel = DbValueToProperty((byte)data.Get("compatibility_level"), CompatibilityLevelType.Sql100);
            this.Collation = (string)data.Get("collation_name");
            this.DataSizeKb = 6144;
            this.DataGrowthKb = (int)data.Get("db_file_main_growth", (Int32?)null);
            this.LogSizeKb = 3072;
            this.LogGrowthPercent = (int)data.Get("db_file_log_growth", (Int32?)null);
            this.IsAnsiNullDefault = (bool)data.Get("is_ansi_null_default_on");
            this.IsAnsiNulls = (bool)data.Get("is_ansi_nulls_on");
            this.IsAnsiPadding = (bool)data.Get("is_ansi_padding_on");
            this.IsAnsiWarnings = (bool)data.Get("is_ansi_warnings_on");
            this.IsArithAbort = (bool)data.Get("is_arithabort_on");
            this.IsAutoClose = (bool)data.Get("is_auto_close_on");
            this.IsAutoCreateStats = (bool)data.Get("is_auto_create_stats_on");
            this.IsAutoShrink = (bool)data.Get("is_auto_shrink_on");
            this.IsAutoUpdateStats = (bool)data.Get("is_auto_update_stats_on");
            this.IsAutoUpdateStatsAsync = (bool)data.Get("is_auto_update_stats_async_on");
            this.IsCursorCloseOnCommit = (bool)data.Get("is_cursor_close_on_commit_on");
            this.CursorDefault = DbValueToProperty((bool)data.Get("is_local_cursor_default"), CursorDefaultType.Local);
            this.IsConcatNullYieldsNull = (bool)data.Get("is_concat_null_yields_null_on");
            this.IsNumericRoundabort = (bool)data.Get("is_numeric_roundabort_on");
            this.IsQuotedIdentifier = (bool)data.Get("is_quoted_identifier_on");
            this.IsRecursiveTriggers = (bool)data.Get("is_recursive_triggers_on");
            this.BrokerMode = DbValueToProperty((bool)data.Get("is_broker_enabled"), BrokerType.Enabled );
            this.DateCorrelationOptimization = (bool)data.Get("is_date_correlation_on");
            this.Parameterization = DbValueToProperty((bool)data.Get("is_parameterization_forced"), ParametrizationType.Forced );
            this.ReadWrite = DbValueToProperty((bool)data.Get("is_read_only"), ReadWriteType.ReadWrite);
            this.RecoveryModel = DbValueToProperty((byte)data.Get("recovery_model"), RecoveryType.Simple);
            this.UserAccess = DbValueToProperty((byte)data.Get("user_access"), UserAccesType.MultiUser);
            this.PageVerifyOption = DbValueToProperty((byte)data.Get("page_verify_option"), PageVerifyType.CheckSum);
            this.DbFileMain = (string)data.Get("db_file_main", null);
            this.SizeKB = (int)data.Get("db_file_size_kb", 0);
        }
        #endregion
        #region Vytvoření databáze, patchování existující
        /// <summary>
        /// Vytvoření databáze v dané connection, pod daným jménem a v daném adresáři.
        /// Ostatní parametry přebírá z this.
        /// Volá se tedy na objektu načteném z deklarace.
        /// </summary>
        /// <param name="connect"></param>
        /// <param name="databaseName"></param>
        /// <param name="directory"></param>
        public void Create(DatabaseConnect connect, string databaseName, string directory)
        {
            StringBuilder script = new StringBuilder();
            this._ScriptCreateDb(databaseName, directory, script);
            connect.ExecuteScript(script.ToString());
        }
        /// <summary>
        /// Zajistí, že databáze (connect.InitialDatabase) bude odpovídat aktuálnímu obrazu v objektu this.
        /// Volá se tedy na objektu načteném z deklarace.
        /// </summary>
        /// <param name="connect"></param>
        public override void Patch(DatabaseConnect connect)
        {
            StringBuilder script = new StringBuilder();
            DbElementDatabase dbRealDatabase = DatabaseCls.DbInfoGetDatabase(connect, connect.InitialDatabase);
            if (dbRealDatabase == null)
                throw new InvalidOperationException("Chyba řízení: databázi není možno Patchovat, v situaci, kdy neexistuje.");
            this._ScriptProperty(dbRealDatabase, script);
            if (script.Length > 0)
                connect.ExecuteScript(script.ToString());
        }
        /// <summary>
        /// Vrátí skript, který zajistí vytvoření databáze podle parametrů.
        /// </summary>
        /// <param name="databaseName">Jméno databáze [bez závorek]</param>
        /// <param name="directory">Adresář, končící zpětným lomítkem, kam budou ukládána data</param>
        /// <returns></returns>
        private void _ScriptCreateDb(string databaseName, string directory, StringBuilder script)
        {
            string ego = Environment.NewLine + "GO";
            script.AppendLine("USE [master]" + ego);
            script.AppendLine("CREATE DATABASE [{{DbName}}] ON PRIMARY");
            script.AppendLine("( NAME = N'{{DbName}}', FILENAME = N'{{DbPath}}{{DbName}}.mdf' , " + this._ScriptDataSize + ", " + this._ScriptDataGrowth + " )");
            script.AppendLine(" LOG ON ");
            script.AppendLine("( NAME = N'{{DbName}}_log', FILENAME = N'{{DbPath}}{{DbName}}_log.ldf' , " + this._ScriptLogSize + ", " + this._ScriptLogGrowth + " )");
            script.AppendLine(" COLLATE " + this.Collation + ego);
            this._ScriptProperty(null, script);
            script.AppendLine("IF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'PRIMARY')");
            script.AppendLine(" ALTER DATABASE [{{DbName}}] MODIFY FILEGROUP [PRIMARY] DEFAULT" + ego);

            string dbName = databaseName;
            string dbPath = directory.Trim();
            if (!dbPath.EndsWith("\\"))
                dbPath += "\\";

            script = script.Replace("{{DbName}}", dbName);
            script = script.Replace("{{DbPath}}", dbPath);
        }
        private string _ScriptDataSize { get { return "SIZE = " + (this.DataSizeKb.HasValue ? this.DataSizeKb.Value.ToString() : "6144") + "KB"; } }
        private string _ScriptDataGrowth { get { return "FILEGROWTH = " + (this.DataGrowthKb.HasValue ? this.DataGrowthKb.Value.ToString() : "2048") + "KB"; } }
        private string _ScriptLogSize { get { return "SIZE = " + (this.LogSizeKb.HasValue ? this.LogSizeKb.Value.ToString() : "2048") + "KB"; } }
        private string _ScriptLogGrowth { get { return "FILEGROWTH = " + (this.LogGrowthPercent.HasValue ? this.LogGrowthPercent.Value.ToString() : "10") + "%"; } }
        /// <summary>
        /// Do stringbuilderu vygeneruje skript, který pro aktuální databázi nastaví její vlastnosti podle zdejších hodnot.
        /// Vstupní parametr dbRealDatabase může být null, pak se generuje skript plný = pro všechny property (pro metodu Create).
        /// </summary>
        /// <param name="dbRealDatabase"></param>
        /// <param name="sb"></param>
        private void _ScriptProperty(DbElementDatabase dbRealDatabase, StringBuilder script)
        {
            string ego = Environment.NewLine + "GO";
            bool create = (dbRealDatabase == null);

            // Připravím si holý lokální skript (pokud vůbec budou nějaké změny):
            StringBuilder sb = new StringBuilder();
            if (create || this.CompatibilityLevel != dbRealDatabase.CompatibilityLevel)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET COMPATIBILITY_LEVEL = " + PropertyToSqlCommand(this.CompatibilityLevel) + ego);
            if (create || this.IsAnsiNullDefault != dbRealDatabase.IsAnsiNullDefault)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET ANSI_NULL_DEFAULT " + PropertyToSqlCommand(this.IsAnsiNullDefault) + ego);
            if (create || this.IsAnsiNulls != dbRealDatabase.IsAnsiNulls)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET ANSI_NULLS " + PropertyToSqlCommand(this.IsAnsiNulls) + ego);
            if (create || this.IsAnsiPadding != dbRealDatabase.IsAnsiPadding)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET ANSI_PADDING " + PropertyToSqlCommand(this.IsAnsiPadding) + ego);
            if (create || this.IsAnsiWarnings != dbRealDatabase.IsAnsiWarnings)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET ANSI_WARNINGS " + PropertyToSqlCommand(this.IsAnsiWarnings) + ego);
            if (create || this.IsArithAbort != dbRealDatabase.IsArithAbort)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET ARITHABORT " + PropertyToSqlCommand(this.IsArithAbort) + ego);
            if (create || this.IsAutoClose != dbRealDatabase.IsAutoClose)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET AUTO_CLOSE " + PropertyToSqlCommand(this.IsAutoClose) + ego);
            if (create || this.IsAutoCreateStats != dbRealDatabase.IsAutoCreateStats)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET AUTO_CREATE_STATISTICS " + PropertyToSqlCommand(this.IsAutoCreateStats) + ego);
            if (create || this.IsAutoShrink != dbRealDatabase.IsAutoShrink)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET AUTO_SHRINK " + PropertyToSqlCommand(this.IsAutoShrink) + ego);
            if (create || this.IsAutoUpdateStats != dbRealDatabase.IsAutoUpdateStats)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET AUTO_UPDATE_STATISTICS " + PropertyToSqlCommand(this.IsAutoUpdateStats) + ego);
            if (create || this.IsCursorCloseOnCommit != dbRealDatabase.IsCursorCloseOnCommit)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET CURSOR_CLOSE_ON_COMMIT " + PropertyToSqlCommand(this.IsCursorCloseOnCommit) + ego);
            if (create || this.CursorDefault != dbRealDatabase.CursorDefault)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET CURSOR_DEFAULT " + PropertyToSqlCommand(this.CursorDefault) + ego);
            if (create || this.IsConcatNullYieldsNull != dbRealDatabase.IsConcatNullYieldsNull)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET CONCAT_NULL_YIELDS_NULL " + PropertyToSqlCommand(this.IsConcatNullYieldsNull) + ego);
            if (create || this.IsNumericRoundabort != dbRealDatabase.IsNumericRoundabort)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET NUMERIC_ROUNDABORT " + PropertyToSqlCommand(this.IsNumericRoundabort) + ego);
            if (create || this.IsQuotedIdentifier != dbRealDatabase.IsQuotedIdentifier)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET QUOTED_IDENTIFIER " + PropertyToSqlCommand(this.IsQuotedIdentifier) + ego);
            if (create || this.IsRecursiveTriggers != dbRealDatabase.IsRecursiveTriggers)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET RECURSIVE_TRIGGERS " + PropertyToSqlCommand(this.IsRecursiveTriggers) + ego);
            if (create || this.BrokerMode != dbRealDatabase.BrokerMode)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET " + PropertyToSqlCommand(this.BrokerMode) + ego);
            if (create || this.IsAutoUpdateStatsAsync != dbRealDatabase.IsAutoUpdateStatsAsync)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET AUTO_UPDATE_STATISTICS_ASYNC " + PropertyToSqlCommand(this.IsAutoUpdateStatsAsync) + ego);
            if (create || this.DateCorrelationOptimization != dbRealDatabase.DateCorrelationOptimization)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET DATE_CORRELATION_OPTIMIZATION " + PropertyToSqlCommand(this.DateCorrelationOptimization) + ego);
            if (create || this.Parameterization != dbRealDatabase.Parameterization)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET PARAMETERIZATION " + PropertyToSqlCommand(this.Parameterization) + ego);
            if (create || this.ReadWrite != dbRealDatabase.ReadWrite)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET " + PropertyToSqlCommand(this.ReadWrite) + ego);
            if (create || this.RecoveryModel != dbRealDatabase.RecoveryModel)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET RECOVERY " + PropertyToSqlCommand(this.RecoveryModel) + ego);
            if (create || this.UserAccess != dbRealDatabase.UserAccess)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET " + PropertyToSqlCommand(this.UserAccess) + ego);
            if (create || this.PageVerifyOption != dbRealDatabase.PageVerifyOption)
                sb.AppendLine("ALTER DATABASE [{{DbName}}] SET PAGE_VERIFY " + PropertyToSqlCommand(this.PageVerifyOption) + ego);
            
            // Pokud reálně byly vydány nějaké příkazy ALTER, pak zdejší skript (z lokálního skriptu v (sb)) 
            //  opatřím přepnutím databáze a vložím do vnějšího skriptu:
            if (sb.Length > 0)
            {
                script.AppendLine("USE [master]" + ego);
                script.Append(sb.ToString());
                script.AppendLine("USE [{{DbName}}]" + ego);
            }
        }
        /// <summary>
        /// Vrátí skript, který na dané databázi vytvoří schema
        /// </summary>
        /// <param name="databaseName">Název databáze</param>
        /// <param name="schemaName"></param>
        /// <param name="authorization"></param>
        /// <returns></returns>
        public string GenerateScriptCreateSchema(string databaseName, string schemaName, string authorization)
        {
            string cmd = @"{{UseDbName}}CREATE SCHEMA [{{SchemaName}}]{{Authorization}}
GO";
            cmd = cmd.Replace("{{UseDbName}}", (databaseName == null ? "" : "USE [" + databaseName + "]" + Environment.NewLine + "GO" + Environment.NewLine));
            cmd = cmd.Replace("{{SchemaName}}", schemaName);
            cmd = cmd.Replace("{{Authorization}}", (authorization == null ? "" : " AUTHORIZATION [" + authorization + "]"));

            return cmd;
        }
        #endregion
        #region Static servis
        /// <summary>
        /// Vrátí SQL SELECT pro načtení všech objektů
        /// </summary>
        public static string SqlSelect
        {
            get
            {
                string property1 = @"compatibility_level, collation_name, 
is_ansi_null_default_on, is_ansi_nulls_on, is_ansi_padding_on, is_ansi_warnings_on, is_arithabort_on, is_auto_close_on, is_auto_create_stats_on,
is_auto_shrink_on, is_auto_update_stats_on, is_cursor_close_on_commit_on, is_local_cursor_default, is_concat_null_yields_null_on,
is_numeric_roundabort_on, is_quoted_identifier_on, is_recursive_triggers_on, is_broker_enabled, is_auto_update_stats_async_on, 
is_date_correlation_on, is_parameterization_forced, is_read_only, recovery_model, user_access, page_verify_option, ";
                
                string property2 = @"state, state_desc, is_in_standby, is_cleanly_shutdown, is_supplemental_logging_enabled, snapshot_isolation_state, 
is_read_committed_snapshot_on, is_fulltext_enabled, is_trustworthy_on, is_db_chaining_on, is_master_key_encrypted_by_server,
is_published, is_subscribed, is_merge_published, is_distributor, is_sync_with_backup, service_broker_guid, log_reuse_wait,
log_reuse_wait_desc, is_cdc_enabled, is_encrypted, is_honor_broker_priority_on, ";
                
                return @"SELECT database_id as id, name, " + property1 + @"
 (select top 1 mf.physical_name from sys.master_files mf where mf.database_id = sys.databases.database_id and mf.type = 0) db_file_main,
 (select top 1 mf.growth from sys.master_files mf where mf.database_id = sys.databases.database_id and mf.type = 0) db_file_main_growth,
 (select top 1 mf.is_percent_growth from sys.master_files mf where mf.database_id = sys.databases.database_id and mf.type = 0) db_file_main_is_percent_growth,
 (select top 1 mf.growth from sys.master_files mf where mf.database_id = sys.databases.database_id and mf.type <> 0) db_file_log_growth,
 (select top 1 mf.is_percent_growth from sys.master_files mf where mf.database_id = sys.databases.database_id and mf.type <> 0) db_file_log_is_percent_growth,
 (select sum(mf.size*8) from sys.master_files mf where mf.database_id = sys.databases.database_id) db_file_size_kb
FROM sys.databases
" + TOKEN_WHERE;
            }
        }
        /// <summary>
        /// Vrátí SQL select, který vrátí data jedné databáze dané jejím názvem
        /// </summary>
        public static string SqlSelectForName(string databaseName) { return SqlSelect.Replace(TOKEN_WHERE, "WHERE name = '" + databaseName + "'"); }
        /// <summary>
        /// Vrátí SQL FILTR pro výběr jen ne-systémových databází
        /// </summary>
        public static string SqlFilterNoSystem
        {
            get
            {
                string names = "";
                foreach (string name in SystemDatabaseNames.Concat(ReportDatabaseNames))
                    names += ", '" + name + "'";
                return " WHERE name not in (" + names.Substring(2) + ")";
            }
        }
        /// <summary>
        /// Vrátí true, pokud dané jméno databáze odpovídá systémové databázi
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static bool IsSystemDatabaseName(string name) { return (SystemDatabaseNames.Any(n => String.Equals(n, name, StringComparison.InvariantCultureIgnoreCase))); }
        /// <summary>
        /// Vrátí true, pokud dané jméno databáze odpovídá report databázi
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static bool IsReportDatabaseName(string name) { return (ReportDatabaseNames.Any(n => String.Equals(n, name, StringComparison.InvariantCultureIgnoreCase))); }
        /// <summary>
        /// Obsahuje názvy databází, které používá systém
        /// </summary>
        public static string[] SystemDatabaseNames { get { return new string[] { "master", "model", "tempdb", "msdb" }; } }
        /// <summary>
        /// Obsahuje názvy databází, které používá systém
        /// </summary>
        public static string[] ReportDatabaseNames { get { return new string[] { "ReportServer", "ReportServerTempDB" }; } }
        #endregion
    }
    #endregion
    #region DbSecurity
    /*
      
-- ???
select * from sys.credentials 
select * from sys.login_token
select * from sys.user_token 

-- Database-Level Views
select * from sys.database_permissions	  -- kdo co může/nemůže dělat v databázi
select * from sys.database_role_members   -- db role a osoby které do té role patří
select * from sys.database_principals     -- uživatelé, role - názvy, id, typ, default schema
select * from sys.master_key_passwords	  -- ???


-- Server-Level Views
select * from sys.server_permissions 	  -- kdo co může/nemůže dělat na serveru
select * from sys.sql_logins              -- loginy k serveru, typy, atd
select * from sys.server_principals       -- uživatelé, role - názvy, id, typ, default schema - k serveru
select * from sys.system_components_surface_area_configuration 
select * from sys.server_role_members     -- db role a osoby které do té role patří 

ASOL script:
------------

USE [GreenStandard43CZ]

--addlogin je pro vsechny db
create login lcs with password = 'saruman'
create login netadmin with password = 'saruman'
create login netuser with password = 'saruman'

--adduser je pro akt. db
create user lcs for login lcs with default_schema = lcs
create user netadmin for login netadmin with default_schema = lcs
create user netuser for login netuser with default_schema = lcs
go

sp_addRoleMember 'admin', 'lcs'
go
sp_addRoleMember 'uzivatele', 'lcs'
go
sp_addRoleMember 'admin', 'netadmin'
go
sp_addRoleMember 'uzivatele', 'netadmin'
go
sp_addRoleMember 'uzivatele', 'netuser'
go
sp_addRoleMember 'db_owner', 'netadmin'
go

--prirazeni loginu
sp_change_users_login 'update_one','netuser','netuser'
go
sp_change_users_login 'update_one','netadmin','netadmin'
go
sp_change_users_login 'update_one','lcs','lcs'
go

     */
    #endregion
    #region class DbElementSchema : informace o schematu
    /// <summary>
    /// Data o schematu (vlastník tabulek)
    /// </summary>
    public class DbElementSchema : DbElement
    { 
        #region Konstrukce, proměnné, tvorba z XML deklarace
        public DbElementSchema()
        { }
        public static DbElementSchema CreateFromXml(XElement element)
        {
            if (element == null) return null;
            if (element.Name.LocalName != "db_schema") return null;
            DbElementSchema schema = new DbElementSchema();
            schema.Name = element.GetAttributeString("Name");
            schema.Principal = element.GetAttributeString("Principal");
            return schema;
        }
        #endregion
        #region Property a Fill
        /// <summary>
        /// Principal (= Authorization)
        /// </summary>
        public string Principal { get; protected set; }
        /// <summary>
        /// Načtení vlastních dat nad rámec předka DbElement
        /// </summary>
        /// <param name="data"></param>
        protected override void Fill(RecordFillInfo data)
        {   // sloupce id a name byly načteny v metodě, která nás volá, ve třídě DbElement.
            this.Principal = (string)data.Get("principal");
        }
        #endregion
        #region Patchování objektu (vytvoření nového / úprava existujícího)
        /// <summary>
        /// Zajistí, že konkrétní objekt (odpovídající this) bude mít v databázi shodný stav, jaký má objekt this.
        /// Typicky: this je načteno z deklarace (XML definice obrazu databáze), 
        /// pro svou potřebu si skrz connaction načte reálný obraz objektu, porovná rozdíly a na rozdíly vytvoří skript a spustí jej.
        /// Skript zajistí shodu vlastností.
        /// Tato verze je určena k přepsání na potomku.
        /// </summary>
        /// <param name="connect"></param>
        public override void Patch(DatabaseConnect connect)
        {
            StringBuilder script = new StringBuilder();
            DbElementSchema dbSchemaReal = DatabaseCls.DbInfoGetSchema(connect, this.Name);
            if (dbSchemaReal == null)
                this._ScriptCreate(script);
            else
                this._ScriptAlter(dbSchemaReal, script);
            if (script.Length > 0)
                connect.ExecuteScript(script.ToString());
        }
        /// <summary>
        /// Vytvoří skript CREATE SCHEMA
        /// </summary>
        /// <param name="script"></param>
        private void _ScriptCreate(StringBuilder script)
        {
            script.AppendLine("CREATE SCHEMA [" + this.Name +"]");
            if (!String.IsNullOrEmpty(this.Principal))
                script.AppendLine(" AUTHORIZATION [" + this.Principal + "]");
            script.AppendLine("GO");
        }
        /// <summary>
        /// Vytvoří skript ALTER SCHEMA, pokud toho bude zapotřebí.
        /// </summary>
        /// <param name="dbSchemaReal"></param>
        /// <param name="script"></param>
        private void _ScriptAlter(DbElementSchema dbSchemaReal, StringBuilder script)
        {
            if (!String.IsNullOrEmpty(this.Principal) && (String.IsNullOrEmpty(dbSchemaReal.Principal) || !String.Equals(this.Principal, dbSchemaReal.Principal, StringComparison.InvariantCulture)))
            { /*   Změna Principal nejde vyskriptovat :-(   */ }
        }
        #endregion
        #region Static služby
        /// <summary>
        /// SQL SELECT pro načtení všech objektů.
        /// Namísto fráze WHERE obsahuje token {{Where}} (= konstanta TOKEN_WHERE).
        /// </summary>
        public static string SqlSelect
        { get { return @"SELECT sch.schema_id as id, sch.name, prn.name as principal FROM sys.schemas sch left outer join sys.schemas prn on prn.schema_id = sch.schema_id " + TOKEN_WHERE; } }
        /// <summary>
        /// Alias tabulky (master), z níž se načítají data, bez tečky na konci
        /// </summary>
        public static string SqlSelectTable { get { return "sch"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje ID schematu.
        /// </summary>
        public static string SqlSelectIdColumn { get { return "sch.schema_id"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje název schematu.
        /// </summary>
        public static string SqlSelectNameColumn { get { return "sch.name"; } }
        /// <summary>
        /// Vrátí SQL select, který vrátí záznam schematu daný jeho ID
        /// </summary>
        public static string SqlSelectForId(int schemaId)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE sch.schema_id = " + schemaId.ToString());
        }
        /// <summary>
        /// Vrátí SQL select, který vrátí záznam schematu daný jeho názvem
        /// </summary>
        public static string SqlSelectForName(string schemaName)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE sch.name = '" + schemaName + "'");
        }
        #endregion
    }
    #endregion
    #region class DbElementTable : informace o tabulce
    /// <summary>
    /// Data o tabulce
    /// </summary>
    public class DbElementTable : DbElement
    {
        #region Konstrukce, proměnné, tvorba z XML deklarace
        public DbElementTable()
        {
            this._ColumnList = new List<DbElementColumn>();
            this._IndexList = new List<DbElementIndex>();
        }
        public static DbElementTable CreateFromXml(XElement element)
        {
            if (element == null) return null;
            if (element.Name.LocalName != "table") return null;
            DbElementTable table = new DbElementTable();
            table.Schema = element.GetAttributeString("Owner");
            table.Name = element.GetAttributeString("Name");
            table.Storage = element.GetAttributeString("Storage");
            table.Comment = element.GetAttributeString("Comment");
            table.LockEscalation = XmlToProperty(element, "LockEscalation", LockEscalationType.None);
            foreach (XElement subElement in element.Elements())
            {
                switch (subElement.Name.LocalName)
                {
                    case "column":
                        table.AddColumnFromXml(subElement);
                        break;
                    case "index":
                        table.AddIndexFromXml(subElement);
                        break;
                }
            }
            return table;
        }
        private void AddColumnFromXml(XElement element)
        {
            DbElementColumn column = DbElementColumn.CreateFromXml(element, this);
            if (column != null)
                this._ColumnList.Add(column);
        }
        private void AddIndexFromXml(XElement element)
        {
            DbElementIndex index = DbElementIndex.CreateFromXml(element, this);
            if (index != null)
                this._IndexList.Add(index);
        }
        private List<DbElementColumn> _ColumnList;
        private List<DbElementIndex> _IndexList;
        #endregion
        #region Enums a konverze
        #region Enums
        public enum LockEscalationType
        {
            None,
            /// <summary>
            /// AUTO
            /// </summary>
            Auto,
            /// <summary>
            /// TABLE
            /// </summary>
            Table,
            /// <summary>
            /// DISABLE 
            /// </summary>
            Disable
        }
        #endregion
        #region Konverze enumů, db hodnot, XML hodnot, db settingu
        private static LockEscalationType XmlToProperty(XElement element, string attributeName, LockEscalationType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value.ToLower())
                {
                    case "none": return LockEscalationType.None;
                    case "auto": return LockEscalationType.Auto;
                    case "table": return LockEscalationType.Table;
                    case "disable": return LockEscalationType.Disable;
                }
            }
            return defaultValue;
        }
        private static string PropertyToSqlCommand(LockEscalationType value)
        {
            switch (value)
            {
                case LockEscalationType.Auto: return "AUTO";
                case LockEscalationType.Table: return "TABLE";
                case LockEscalationType.Disable: return "DISABLE";
            }
            return "AUTO";
        }
        #endregion
        #endregion
        #region Property a Fill
        /// <summary>
        /// Název tabulky včetně vlastníka
        /// </summary>
        public string TableName { get { return JoinObjectNames(this.Schema, this.Name); } }
        /// <summary>
        /// Název tabulky včetně vlastníka do skriptů = Quotted() = "[owner].[table]"
        /// </summary>
        internal string ScriptTableName { get { return Quotted(this.Schema, this.Name); } }
        /// <summary>
        /// Úložiště tabulky (databáze)
        /// </summary>
        public string Storage { get; protected set; }
        /// <summary>
        /// SchemaId
        /// </summary>
        public string Schema { get; protected set; }
        /// <summary>
        /// Typ eskalace zámků
        /// </summary>
        public LockEscalationType LockEscalation { get; protected set; }
        /// <summary>
        /// Sloupce tabulky
        /// </summary>
        public List<DbElementColumn> ColumnList { get { return this._GetColumnList(); } }
        /// <summary>
        /// Sloupce tabulky
        /// </summary>
        public List<DbElementIndex> IndexList { get { return this._GetIndexList(); } }
        /// <summary>
        /// Načtení vlastních dat nad rámec předka DbElement
        /// </summary>
        /// <param name="data"></param>
        protected override void Fill(RecordFillInfo data)
        {   // sloupce id a name byly načteny v metodě, která nás volá, ve třídě DbElement.
            this.Schema = (string)data.Get("schema_name");


            this._ColumnList = null;
            this._IndexList = null;
        }
        #endregion
        #region Patchování objektu (vytvoření nového / úprava existujícího)
        /// <summary>
        /// Zajistí, že konkrétní objekt (odpovídající this) bude mít v databázi shodný stav, jaký má objekt this.
        /// Typicky: this je načteno z deklarace (XML definice obrazu databáze), 
        /// pro svou potřebu si skrz connaction načte reálný obraz objektu, porovná rozdíly a na rozdíly vytvoří skript a spustí jej.
        /// Skript zajistí shodu vlastností.
        /// Tato verze je určena k přepsání na potomku.
        /// </summary>
        /// <param name="connect"></param>
        public override void Patch(DatabaseConnect connect)
        {
            StringBuilder script = new StringBuilder();
            DbElementTable dbTableReal = DatabaseCls.DbInfoGetTable(connect, this.TableName, true);
            if (dbTableReal == null)
                this._ScriptCreate(script);
            else
                this._ScriptAlter(dbTableReal, script);
            if (script.Length > 0)
                connect.ExecuteScript(script.ToString());
        }
        /// <summary>
        /// Vytvoří skript CREATE TABLE
        /// </summary>
        /// <param name="script"></param>
        private void _ScriptCreate(StringBuilder script)
        {
            script.AppendLine("CREATE TABLE " + this.ScriptTableName);
            script.AppendLine("  (");
            List<DbElementColumn> columns = this.ColumnList;
            List<DbElementIndex> primaryKeys = this.IndexList.Where(idx => idx.IsPrimaryKey).ToList();
            bool hasPrimaryKey = (primaryKeys.Count > 0);
            
            // Columns:
            int lastColumnIdx = columns.Count - 1;
            for (int i = 0; i < this.ColumnList.Count; i++)
            {
                bool addColon = (i < lastColumnIdx || hasPrimaryKey);
                script.AppendLine("    " + columns[i].ScriptCreate + (addColon ? "," : ""));
            }

            // Primary key[s]:
            int lastPrimaryIdx = primaryKeys.Count - 1;
            for (int i = 0; i < primaryKeys.Count; i++)
            {
                bool addColon = (i < lastPrimaryIdx);
                script.AppendLine("    " + primaryKeys[i].ScriptCreate + (addColon ? "," : ""));
            }
            // Create table ends:
            script.AppendLine("  )  ON [PRIMARY]");
            script.AppendLine("GO");

            // Indexes:
            List<DbElementIndex> indexes = this.IndexList.Where(idx => !idx.IsPrimaryKey).ToList();
            foreach (DbElementIndex index in indexes)
            {
                script.AppendLine(index.ScriptCreate);
                script.AppendLine("GO");
            }

            // TableProperties:
            this._ScriptProperties(null, script);
        }
        /// <summary>
        /// Vytvoří skript ALTER TABLE, pokud toho bude zapotřebí.
        /// </summary>
        /// <param name="dbTableReal"></param>
        /// <param name="script"></param>
        private void _ScriptAlter(DbElementTable dbTableReal, StringBuilder script)
        {
           
        }
        /// <summary>
        /// Do skriptu přidá skriptování vlastností tabulky (LockEscalation)
        /// </summary>
        /// <param name="dbRealTable"></param>
        /// <param name="script"></param>
        private void _ScriptProperties(DbElementTable dbRealTable, StringBuilder script)
        {
            string ego = Environment.NewLine + "GO";
            bool create = (dbRealTable == null);

            // Připravím si holý lokální skript (pokud vůbec budou nějaké změny):
            StringBuilder sb = new StringBuilder();
            if (create || this.LockEscalation != dbRealTable.LockEscalation)
                sb.AppendLine("ALTER TABLE " + this.ScriptTableName + " SET (LOCK_ESCALATION = " + PropertyToSqlCommand(this.LockEscalation) + ")" + ego);

            // Pokud byly reálně vydány nějaké příkazy ALTER, pak zdejší skript (z lokálního skriptu v (sb)) vložím do vnějšího skriptu:
            if (sb.Length > 0)
            {
                script.Append(sb.ToString());
            }
        }
        #endregion
        #region On-demand donačítání sloupců a indexů
        /// <summary>
        /// Zajistí načtení dalšího obsahu tohoto objektu (sloupce, indexy, texty, práva, atd).
        /// Tato varianta využívá explicitně dodanou connection.
        /// Dovoluje načíst data povinně = i poté, kdy už byla načtena (lze zadat force = true).
        /// Tato varianta je určená k přepsání, je to vlastní výkonná metoda.
        /// </summary>
        /// <param name="connect">Explicitně dodaná connection</param>
        /// <param name="force">Načíst povinně, i když už jsou data načtena</param>
        public override void LoadContent(DatabaseConnect connect, bool force)
        {
            if (force)
            {   // force = true: donutíme načíst data bez ohledu na současný stav:
                this._ColumnList = null;
                this._IndexList = null;
            }
            this._GetColumnList(connect);
            this._GetIndexList(connect);
        }
        private List<DbElementColumn> _GetColumnList()
        {
            if (this._ColumnList != null) return this._ColumnList;
            return this._GetColumnList(Stator.Database.GetConnection());
        }
        private List<DbElementColumn> _GetColumnList(DatabaseConnect connect)
        {
            if (this._ColumnList != null) return this._ColumnList;
            string commandText = DbElementColumn.SqlSelectForTableName(this.TableName);
            List<DbElementColumn> columns = DatabaseCls.ReadList<DbElementColumn>(commandText, connect);
            foreach (DbElementColumn column in columns)
                column.Table = this;
            this._ColumnList = columns;
            return this._ColumnList;
        }
        private List<DbElementIndex> _GetIndexList()
        {
            if (this._IndexList != null) return this._IndexList;
            return this._GetIndexList(Stator.Database.GetConnection());
        }
        private List<DbElementIndex> _GetIndexList(DatabaseConnect connect)
        {
            if (this._IndexList != null) return this._IndexList;
            string commandText = DbElementIndex.SqlSelectForTableName(this.TableName);
            List<DbElementIndex> indexes = DatabaseCls.ReadList<DbElementIndex>(commandText, connect);
            indexes = DbElementIndex.MergeKeys(indexes);
            foreach (DbElementIndex index in indexes)
                index.Table = this;
            this._IndexList = indexes;
            return this._IndexList;
        }
        #endregion
        #region Static služby
        /// <summary>
        /// SQL SELECT pro načtení všech objektů.
        /// Namísto fráze WHERE obsahuje token {{Where}} (= konstanta TOKEN_WHERE).
        /// Do filtru je třeba vložit i typ objektu (typicky sys.objects.type = 'U').
        /// Filtrovat lze podle ID objektu nebo podle jeho názvu, k tomu slouží DB funkce object_id('owner.name')
        /// </summary>
        public static string SqlSelect
        {
            get
            {
                return @"SELECT obj.object_id as id, sch.name as schema_name, obj.name
FROM sys.objects obj left outer join sys.schemas sch on sch.schema_id = obj.schema_id
" + TOKEN_WHERE;
            }
        }
        /// <summary>
        /// Alias tabulky (master), z níž se načítají data, bez tečky na konci
        /// </summary>
        public static string SqlSelectTable { get { return "sys.objects"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje ID tabulky.
        /// </summary>
        public static string SqlSelectIdColumn { get { return "obj.object_id"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje název tabulky (bez vlastníka).
        /// </summary>
        public static string SqlSelectNameColumn { get { return "obj.name"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje název schematu (vlastníka objektu).
        /// </summary>
        public static string SqlSelectSchemaNameColumn { get { return "sch.name"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje typ objektu.
        /// </summary>
        public static string SqlSelectObjectTypeColumn { get { return "obj.type"; } }
        /// <summary>
        /// Vrátí SQL select, který vrátí data tabulky dané jejím ID
        /// </summary>
        public static string SqlSelectForId(int tableId)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE obj.object_id = " + tableId.ToString());
        }
        /// <summary>
        /// Vrátí SQL select, který vrátí data tabulky dané jejím názvem (vlastník.název)
        /// </summary>
        public static string SqlSelectForName(string tableName)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE obj.object_id = object_id('" + tableName + "')");
        }
        #endregion
    }
    #endregion
    #region class DbElementColumn : informace o sloupci tabulky
    /// <summary>
    /// Data o sloupci tabulky
    /// </summary>
    public class DbElementColumn : DbElement
    {
        #region Konstrukce, proměnné, tvorba z XML deklarace
        public DbElementColumn()
        { }
        public static DbElementColumn CreateFromXml(XElement element)
        {
            return CreateFromXml(element, null);
        }
        public static DbElementColumn CreateFromXml(XElement element, DbElementTable table)
        {
            if (element == null) return null;
            if (element.Name.LocalName != "column") return null;
            DbElementColumn column = new DbElementColumn();
            if (table != null)
            {
                column._Table = table;
                column.Id = table.ColumnList.Count + 1;
            }
            column.Name = element.GetAttributeString("Name");
            column.TypeName = element.GetAttributeString("Type");
            column.MaxLength = element.GetAttributeInt32("Length");
            column.Precision = element.GetAttributeInt32("Length");
            column.Scale = element.GetAttributeInt32("Decimal");
            column.CollationName = element.GetAttributeString("CollationName");
            column.IsNullable = element.GetAttributeBoolean("Null");
            column.IsIdentity = element.GetAttributeBoolean("Identity");
            column.DefaultValue = element.GetAttributeString("DefaultValue");
            column.Comment = element.GetAttributeString("Comment");
            return column;
        }
        private DbElementTable _Table;
        #endregion
        #region Property a Fill
        /// <summary>
        /// DataType
        /// </summary>
        protected int TableId { get; set; }
        /// <summary>
        /// Tabulka, do níž sloupec patří
        /// </summary>
        public DbElementTable Table { get { return this._Table; } set { if (this._Table == null) this._Table = value; } }
        /// <summary>
        /// DataType
        /// </summary>
        public string TypeName { get; protected set; }
        /// <summary>
        /// MaxLength : má význam pro stringové sloupce
        /// </summary>
        public int MaxLength { get; protected set; }
        /// <summary>
        /// Precision : u numerických sloupců počet dekadických míst celkem (počet číslic celkem)
        /// </summary>
        public int Precision { get; protected set; }
        /// <summary>
        /// Scale : u numerických sloupců počet desetinných míst (za tečkou)
        /// </summary>
        public int Scale { get; protected set; }
        /// <summary>
        /// CollationName
        /// </summary>
        public string CollationName { get; protected set; }
        /// <summary>
        /// IsNullable
        /// </summary>
        public bool IsNullable { get; protected set; }
        /// <summary>
        /// IsIdentity
        /// </summary>
        public bool IsIdentity { get; protected set; }
        /// <summary>
        /// DefaultValue
        /// </summary>
        public string DefaultValue { get; protected set; }
        /// <summary>
        /// Načtení vlastních dat nad rámec předka DbElement
        /// </summary>
        /// <param name="data"></param>
        protected override void Fill(RecordFillInfo data)
        {   // sloupce id a name byly načteny v metodě, která nás volá, ve třídě DbElement.
            this.TableId = (int)data.Get("table_id");
            this.TypeName = (string)data.Get("type_name");
            this.MaxLength = (short)data.Get("max_length");
            this.Precision = (byte)data.Get("precision");
            this.Scale = (byte)data.Get("scale");
            this.CollationName = (string)data.Get("collation_name");
            this.IsNullable = (bool)data.Get("is_nullable");
            this.IsIdentity = (bool)data.Get("is_identity");
            this.DefaultValue = (string)data.Get("default_definition");
        }
        #endregion
        #region Patchování objektu (vytvoření nového / úprava existujícího)
        /// <summary>
        /// Vrátí skript pro vytvoření sloupce, do příkazu CREATE TABLE.
        /// Obsahuje název sloupce, typ, identity, null, default.
        /// Nezačíná mezerami, nekončí čárkou ani EOL.
        /// </summary>
        /// <returns></returns>
        internal string ScriptCreate
        {
            get { return this.ScriptName + " " + this.ScriptType + this.ScriptIdentity + this.ScriptNull + this.ScriptDefValue; }
        }
        /// <summary>
        /// String obsahující quotted název sloupce (v hranatých závorkách [])
        /// </summary>
        protected string ScriptName
        { get { return Quotted(this.Name); } }
        /// <summary>
        /// String obsahující typ a délku: "[varbinary] (max)"
        /// </summary>
        protected string ScriptType
        {
            get
            {
                string type = (this.TypeName == null ? "" : this.TypeName.Trim().ToLower());
                string result = Quotted(type);
                switch (type)
                {   // Pro některé typy přidám délku a přesnost:
                    case "bit":
                    case "tinyint":
                    case "smallint":
                    case "int":
                    case "bigint":
                    case "datetime":
                    case "smalldatetime":
                    case "date":
                    case "time":
                    case "datetimeoffset":
                        // Tyto nemají nic:
                        break;

                    case "char":
                    case "varchar":
                    case "text":
                    case "nchar":
                    case "nvarchar":
                    case "ntext":
                    case "xml":
                        // Stringové a XML mají MaxLength:
                        result += " " + (this.MaxLength > 0 ? "(" + this.MaxLength.ToString() + ")" : (this.MaxLength < 0 ? "(max)" : ""));
                        break;

                    case "binary":
                    case "varbinary":
                    case "image":
                    case "sql_variant":
                        // Binární stejně jako stringové:
                        result += " " + (this.MaxLength > 0 ? "(" + this.MaxLength.ToString() + ")" : (this.MaxLength < 0 ? "(max)" : ""));
                        break;

                    case "timestamp":
                    case "uniqueidentifier":
                        // Tyto nemají nic:
                        break;

                    default:
                        // Ostatní podle toho, co mají vyplněno:
                        bool addBracket = false;
                        if (this.MaxLength != 0)
                        {
                            result += " (" + (this.MaxLength > 0 ? this.MaxLength.ToString() : "max");
                            addBracket = true;
                        }
                        else if (this.Precision != 0)
                        {
                            result += " (" + (this.Precision > 0 ? this.Precision.ToString() : "max");
                            addBracket = true;
                        }
                        if (addBracket)
                        {
                            if (this.Scale > 0)
                                result += "," + this.Scale.ToString();
                            result += ")";
                        }
                        break;

                }
                return result;
            }
        }
        /// <summary>
        /// String obsahující " IDENTITY ()" nebo ""
        /// </summary>
        protected string ScriptIdentity
        { get { return (this.IsIdentity ? " IDENTITY(1,1)" : ""); } }
        /// <summary>
        /// String obsahující " NULL" nebo " NOT NULL"
        /// </summary>
        protected string ScriptNull
        { get { return (this.IsNullable ? " NULL" : " NOT NULL"); } }
        /// <summary>
        /// String obsahující defaultní hodnotu (tak jak je uvedená v this.DefaultValue) nebo ""
        /// </summary>
        protected string ScriptDefValue
        { get { return (String.IsNullOrEmpty(this.DefaultValue) ? "" : " " + this.DefaultValue); } }
        #endregion
        #region Static služby
        /// <summary>
        /// SQL SELECT pro načtení všech objektů.
        /// Namísto fráze WHERE obsahuje token {{Where}} (= konstanta TOKEN_WHERE).
        /// </summary>
        public static string SqlSelect
        {
            get
            {
                return @"SELECT sys.columns.column_id as id, sys.columns.object_id as table_id,sys.columns.name as name, sys.types.name as type_name,
sys.columns.max_length,sys.columns.precision,sys.columns.scale,sys.columns.collation_name,sys.columns.is_nullable,sys.columns.is_identity,sys.default_constraints.definition as default_definition 
FROM sys.columns 
left outer join sys.types on sys.types.user_type_id = sys.columns.user_type_id
left outer join sys.default_constraints on sys.default_constraints.object_id = sys.columns.default_object_id
" + TOKEN_WHERE + @" ORDER BY table_id, id";
            }
        }
        /// <summary>
        /// Alias tabulky (master), z níž se načítají data, bez tečky na konci
        /// </summary>
        public static string SqlSelectTable { get { return "sys.columns"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje ID tabulky, do níž patří sloupce. Pro filtrování sloupců jedné tabulky.
        /// </summary>
        public static string SqlSelectTableIdColumn { get { return "sys.columns.object_id"; } }
        /// <summary>
        /// Vrátí SQL select, který vrátí sloupce tabulky dané jejím ID
        /// </summary>
        public static string SqlSelectForTableId(int tableId)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE sys.columns.object_id = " + tableId.ToString());
        }
        /// <summary>
        /// Vrátí SQL select, který vrátí sloupce tabulky dané jejím názvem
        /// </summary>
        public static string SqlSelectForTableName(string tableName)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE sys.columns.object_id = object_id('" + tableName + "')");
        }
        #endregion
    }
    #endregion
    #region class DbElementIndex : informace o indexu tabulky
    /// <summary>
    /// Data o indexu tabulky
    /// </summary>
    public class DbElementIndex : DbElement
    {
        #region Konstrukce, proměnné, tvorba z XML deklarace
        public DbElementIndex()
        {
            this.IndexKeyList = new List<DbElementIndexKey>();
            this._IndexKey = null;
        }
        private string _IndexKey;
        private string _IncludeKey;
        public static DbElementIndex CreateFromXml(XElement element)
        {
            return CreateFromXml(element, null);
        }
        public static DbElementIndex CreateFromXml(XElement element, DbElementTable table)
        {
            if (element == null) return null;
            if (element.Name.LocalName != "index") return null;
            DbElementIndex index = new DbElementIndex();
            if (table != null)
            {
                index._Table = table;
                index.Id = (table.IndexList.Count + 1);
            }
            index.Name = element.GetAttributeString("Name");
            index.IndexType = XmlToProperty(element, "IndexType", IndexTypeType.NonClustered);
            index.IsUnique = element.GetAttributeBoolean("IsUnique");
            index.IsPrimaryKey = element.GetAttributeBoolean("IsPrimaryKey");
            index.IsUniqueConstraint = false;
            index.DataSpaceId = 1;
            index.IgnoreDupKey = false;
            index.IndexKey = element.GetAttributeString("Columns");
            index.IncludeKey = element.GetAttributeString("Include");
            index.Comment = element.GetAttributeString("Comment");
            return index;
        }
        private DbElementTable _Table;
        #endregion
        #region Enums a konverze
        #region Enums
        public enum IndexTypeType
        {
            None,
            /// <summary>
            /// 0: Heap
            /// </summary>
            Heap,
            /// <summary>
            /// 1: Clustered
            /// </summary>
            Clustered,
            /// <summary>
            /// 2: NonClustered
            /// </summary>
            NonClustered
        }
        #endregion
        #region Konverze enumů, db hodnot, XML hodnot, db settingu
        private static IndexTypeType DbValueToProperty(byte indexType, IndexTypeType defaultValue)
        {
            switch (indexType)
            {
                case 0: return IndexTypeType.Heap;
                case 1: return IndexTypeType.Clustered;
                case 2: return IndexTypeType.NonClustered;
            }
            return defaultValue;
        }
        private static IndexTypeType XmlToProperty(XElement element, string attributeName, IndexTypeType defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                switch (value.ToLower())
                {
                    case "heap": return IndexTypeType.Heap;
                    case "clustered": return IndexTypeType.Clustered;
                    case "nonclustered": return IndexTypeType.NonClustered;
                }
            }
            return defaultValue;
        }
        private static string PropertyToSqlCommand(IndexTypeType value)
        {
            switch (value)
            {
                case IndexTypeType.Heap: return "HEAP";
                case IndexTypeType.Clustered: return "CLUSTERED";
                case IndexTypeType.NonClustered: return "NONCLUSTERED";
            }
            return "NONCLUSTERED";
        }
        #endregion
        #endregion
        #region Property a Fill
        /// <summary>
        /// DataType
        /// </summary>
        protected int TableId { get; set; }
        /// <summary>
        /// Tabulka, do níž index patří
        /// </summary>
        public DbElementTable Table { get { return this._Table; } set { if (this._Table == null) this._Table = value; } }
        /// <summary>
        /// IndexType
        /// </summary>
        public IndexTypeType IndexType { get; protected set; }
        /// <summary>
        /// IsUnique : vylučuje duplicity
        /// </summary>
        public bool IsUnique { get; protected set; }
        /// <summary>
        /// IsPrimaryKey : jde o primární klíč
        /// </summary>
        public bool IsPrimaryKey { get; protected set; }
        /// <summary>
        /// IsUniqueConstraint 
        /// </summary>
        public bool IsUniqueConstraint { get; protected set; }
        /// <summary>
        /// DataSpaceId
        /// </summary>
        public int DataSpaceId { get; protected set; }
        /// <summary>
        /// IgnoreDupKey
        /// </summary>
        public bool IgnoreDupKey { get; protected set; }
        /// <summary>
        /// Seznam sloupců, které tvoří index
        /// </summary>
        public List<DbElementIndexKey> IndexKeyList { get; protected set; }
        /// <summary>
        /// Klíč indexu = vyjmenované sloupce ve správném pořadí.
        /// Vložení hodnoty null provede invalidaci, příští čtení znovu sestaví textz dat nastřádaných v this.IndexKeyList.
        /// </summary>
        public string IndexKey
        {
            get
            {
                if (this._IndexKey == null)
                    this._IndexKey = this._CreateIndexKey();
                return this._IndexKey;
            }
            set
            { this._IndexKey = value; }
        }
        /// <summary>
        /// Soupis sloupců "include" = vyjmenované sloupce ve správném pořadí
        /// Vložení hodnoty null provede invalidaci, příští čtení znovu sestaví textz dat nastřádaných v this.IndexKeyList.
        /// </summary>
        public string IncludeKey
        {
            get
            {
                if (this._IncludeKey == null)
                    this._IncludeKey = this._CreateIncludeKey();
                return this._IncludeKey;
            }
            set
            { this._IncludeKey = value; }
        }
        /// <summary>
        /// Sestaví text INDEX KEY z dat v this.IndexKeyList
        /// </summary>
        /// <returns></returns>
        private string _CreateIndexKey()
        {
            string colums = "";
            if (this.IndexKeyList != null)
            {
                foreach (DbElementIndexKey indexKey in this.IndexKeyList)
                {
                    if (!indexKey.IsIncluded)
                        colums += ", " + indexKey.ColumnName + (indexKey.IsDescending ? " DESC" : "");
                }
            }
            return (colums.Length == 0 ? "" : colums.Substring(2));
        }
        /// <summary>
        /// Sestaví text INCLUDE COLUMNS z dat v this.IndexKeyList
        /// </summary>
        /// <returns></returns>
        private string _CreateIncludeKey()
        {
            string colums = "";
            if (this.IndexKeyList != null)
            {
                foreach (DbElementIndexKey indexKey in this.IndexKeyList)
                {
                    if (indexKey.IsIncluded)
                        colums += ", " + indexKey.ColumnName;
                }
            }
            return (colums.Length == 0 ? "" : colums.Substring(2));
        }
        /// <summary>
        /// Načtení vlastních dat nad rámec předka DbElement
        /// </summary>
        /// <param name="data"></param>
        protected override void Fill(RecordFillInfo data)
        {   // sloupce id a name byly načteny v metodě, která nás volá, ve třídě DbElement.
            this.TableId = (int)data.Get("table_id");
            this.IndexType = DbValueToProperty((byte)data.Get("index_type"), IndexTypeType.NonClustered);
            this.IsUnique = (bool)data.Get("is_unique");
            this.IsPrimaryKey = (bool)data.Get("is_primary_key");
            this.IsUniqueConstraint = (bool)data.Get("is_unique_constraint");
            this.DataSpaceId = (int)data.Get("data_space_id");
            this.IgnoreDupKey = (bool)data.Get("ignore_dup_key");
            this.IndexKeyList.Add(DbElementIndexKey.CreateFromData(data));     // Z databáze načítám indexy + IndexKey jako více vět, po načtení se provede MergeKeys()
            this.IndexKey = null;
            this.IncludeKey = null;
        }
        /// <summary>
        /// Sloučí IndexKey do jednoho indexu podle IndexId.
        /// Data indexů se z databáze načítají jedním selectem (sys.indexes left outer join sys.index_columns),
        /// takže se načte tolik indexů, kolik je index_columns. Následně tato metoda (MergeKeys) 
        /// vytvoří indexy podle jejich ID, a sloučí do nich jejich Columns (IndexKey).
        /// Poznámka: metodu je možno volat i pro index z váce tabulek, merguje je korektně. 
        /// Rozebrat na tabulky si to musí volající.
        /// </summary>
        /// <param name="indexes"></param>
        /// <returns></returns>
        public static List<DbElementIndex> MergeKeys(IEnumerable<DbElementIndex> indexes)
        {
            Dictionary<ulong, DbElementIndex> indexDict = new Dictionary<ulong, DbElementIndex>();
            foreach (DbElementIndex index in indexes)
            {
                ulong tableId = ((ulong)index.TableId) << 8;
                ulong indexId = ((ulong)index.Id) & 0x00ff;
                ulong key = tableId | indexId;
                DbElementIndex master;
                if (indexDict.TryGetValue(key, out master))
                    master._MergeKeyAdd(index);
                else
                    indexDict.Add(key, index);
            }
            foreach (DbElementIndex master in indexDict.Values)
                master._MergeKeySort();

            return indexDict.Values.ToList();
        }
        /// <summary>
        /// Do this přidá IndexKeyList[0] z dodaného indexu.
        /// Používá se při mergování, viz MergeKeys()
        /// </summary>
        /// <param name="index"></param>
        private void _MergeKeyAdd(DbElementIndex index)
        {
            if (index.IndexKeyList.Count == 1)
            {
                this.IndexKeyList.Add(index.IndexKeyList[0]);
                this.IndexKeyList.Clear();
            }
        }
        /// <summary>
        /// Setřídí sloupce v IndexKeyList podle jejich KeyOrdinal ASC, resetuje lokální paměti
        /// </summary>
        private void _MergeKeySort()
        {
            if (this.IndexKeyList.Count > 1)
                this.IndexKeyList.Sort((a, b) => a.KeyOrdinal.CompareTo(b.KeyOrdinal));
            this._IndexKey = null;
            this._IncludeKey = null;
        }
        #endregion
        #region Patchování objektu (vytvoření nového / úprava existujícího)
        /// <summary>
        /// Vrátí skript pro vytvoření indexu nebo primary key constraint (podle typu), do příkazu CREATE TABLE / ALTER TABLE.
        /// Obsahuje název indexu, typ, identity, sloupce, vlastnosti.
        /// Nezačíná mezerami, nekončí čárkou ani EOL. Neobsahuje GO.
        /// </summary>
        /// <returns></returns>
        internal string ScriptCreate
        {
            get { return (this.IsPrimaryKey ? this.ScriptCreateConstraint : this.ScriptCreateIndex); }
        }
        protected string ScriptCreateIndex
        { get { return "CREATE " + this.ScriptUnique + this.ScriptType + " INDEX " + Quotted(this.Name) + " ON " + this.Table.ScriptTableName + " (" + this.IndexKey + ") " + this.ScriptIndexProperties + " ON [PRIMARY]"; } }
        protected string ScriptCreateConstraint
        { get { return "CONSTRAINT " + Quotted(this.Name) + " PRIMARY KEY" + this.ScriptType + " (" + this.IndexKey + ") " + this.ScriptConstraintProperties + " ON [PRIMARY]"; } }
        protected string ScriptUnique
        { get { return (this.IsUnique ? " UNIQUE" : ""); } }
        protected string ScriptType
        { get { return " " + PropertyToSqlCommand(this.IndexType); } }
        protected string ScriptConstraintProperties
        { get { return " " + ScriptGetProperties(false); } }
        protected string ScriptIndexProperties
        { get { return " " + ScriptGetProperties(true); } }
        protected string ScriptGetProperties(bool forIndex)
        {
            // Constraint : WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF,                       IGNORE_DUP_KEY = OFF,                                    ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 99)
            // Index      : WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)
            // Index      : WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 99)
            bool forConstraint = !forIndex;
            string result = " WITH (" +
                "PAD_INDEX = " + PropertyToSqlCommand(false) + ", " +
                "STATISTICS_NORECOMPUTE = " + PropertyToSqlCommand(false) + ", " +
                (forIndex ? "SORT_IN_TEMPDB = " + PropertyToSqlCommand(false) + ", " : "") +
                "IGNORE_DUP_KEY = " + PropertyToSqlCommand(this.IgnoreDupKey) + ", " +
                (forIndex ? "DROP_EXISTING = " + PropertyToSqlCommand(false) + ", " : "") +
                (forIndex ? "ONLINE = " + PropertyToSqlCommand(false) + ", " : "") +
                "ALLOW_ROW_LOCKS = " + PropertyToSqlCommand(true) + ", " +
                "ALLOW_PAGE_LOCKS = " + PropertyToSqlCommand(true) + ", " +
                "FILLFACTOR = 99)";
            return result;
        }
        #endregion
        #region Static služby
        /// <summary>
        /// SQL SELECT pro načtení všech položek indexů.
        /// Namísto fráze WHERE obsahuje token {{Where}} (= konstanta TOKEN_WHERE).
        /// Pozor, tento SELECT vrací pro jeden každý konkrétní index tolik položek (řádků), kolik ten index obsahuje sloupců (columns).
        /// Vrací je v pořadí, v jakém se objevují v index výrazu (sys.index_columns.key_ordinal).
        /// Načítání indexů (v rámci tabulky nebo jednoho indexu) s tím musí počítat a příchozí záznamy nemůže ukládat slepě jako nové a nové indexy, 
        /// ale podle ID indexu zjistit, zda už takový index (ID) máme, a případně sumarizovat columny (záznamy třídy DbElementIndexKey, metodou DbElementIndex.AddColumnData()).
        /// </summary>
        public static string SqlSelect
        {
            get
            {
                return @"SELECT idx.index_id as id, idx.name as name, idx.object_id as table_id, idx.type as index_type, idx.type_desc as index_type_desc, idx.is_unique, idx.is_primary_key, idx.is_unique_constraint, idx.data_space_id, idx.ignore_dup_key,
 ixc.key_ordinal, col.name as column_name, ixc.partition_ordinal, ixc.is_descending_key, ixc.is_included_column
FROM sys.indexes idx
 left outer join sys.index_columns ixc on ixc.object_id = idx.object_id and ixc.index_id = idx.index_id
 left outer join sys.columns col on col.object_id = idx.object_id and col.column_id = ixc.column_id
" + TOKEN_WHERE + @" ORDER BY idx.object_id, idx.index_id, ixc.key_ordinal";
            }
        }
        /// <summary>
        /// Alias tabulky (master), z níž se načítají data, bez tečky na konci
        /// </summary>
        public static string SqlSelectTable { get { return "idx"; } }
        /// <summary>
        /// Název sloupce (plný), který obsahuje ID tabulky, do níž patří položky indexu. Pro filtrování sloupců jedné tabulky.
        /// </summary>
        public static string SqlSelectTableIdColumn { get { return "idx.object_id"; } }
        /// <summary>
        /// Vrátí SQL select, který načte indexy a jejich sloupce pro tabulku danou jejím ID
        /// </summary>
        public static string SqlSelectForTableId(int tableId)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE idx.object_id = " + tableId.ToString());
        }
        /// <summary>
        /// Vrátí SQL select, který načte indexy a jejich sloupce tabulky dané jejím názvem (vlastník.název)
        /// </summary>
        public static string SqlSelectForTableName(string tableName)
        {
            return SqlSelect.Replace(TOKEN_WHERE, "WHERE idx.object_id = object_id('" + tableName + "')");
        }
        #endregion
    }
    /// <summary>
    /// Data o jednom sloupci v indexu ().
    /// Načítá se jako součást řádku v indexu (DbElementIndex) (je dáno konstrukcí dat v databázi).
    /// Načítaný záznam Index si data o sloupci ukládá do této (DbElementIndexKey) třídy, po načtení se provede Merge klíčů.
    /// Tedy: nejprve se načte pro jeden index tolik (Nnn) záznamů, kolik IndexKey tento index má, vznikne Nnn položek indexů (které mají duplicitní ID).
    /// Poté se zmergují záznamy IndexKey pro jeden každý index.
    /// </summary>
    public class DbElementIndexKey
    {
        public DbElementIndexKey()
        { }
        internal static DbElementIndexKey CreateFromData(RecordFillInfo data)
        {
            if (data == null) return null;
            DbElementIndexKey key = new DbElementIndexKey();
            key.KeyOrdinal = (byte)(data.Get("key_ordinal"));
            key.ColumnName = (string)(data.Get("column_name"));
            key.PartitionOrdinal = (byte)(data.Get("partition_ordinal"));
            key.IsDescending = (bool)data.Get("is_descending_key");
            key.IsIncluded = (bool)data.Get("is_included_column");
            return key;
        }
        /// <summary>
        /// Pořadí sloupce v indexu
        /// </summary>
        public int KeyOrdinal { get; private set; }
        /// <summary>
        /// Název sloupce
        /// </summary>
        public string ColumnName { get; private set; }
        /// <summary>
        /// PartitionOrdinal
        /// </summary>
        public int PartitionOrdinal { get; private set; }
        /// <summary>
        /// Key je DESC
        /// </summary>
        public bool IsDescending { get; private set; }
        /// <summary>
        /// Jde o INCLUDE sloupec
        /// </summary>
        public bool IsIncluded { get; private set; }
    }
    #endregion
    #region abstract class DbElement : bázová třída pro informace o databázových objektech
    /// <summary>
    /// Data o obecném DB objektu
    /// </summary>
    public abstract class DbElement : IRecordFill
    {
        #region Property a Fill
        /// <summary>
        /// ID objektu
        /// </summary>
        public int? Id { get; protected set; }
        /// <summary>
        /// Název objektu
        /// </summary>
        public string Name { get { return this._Name; } protected set { this._OriginalName = value; this._Name = (value == null ? null : value.Replace("*", "")); } }
        private string _Name;
        private string _OriginalName;
        /// <summary>
        /// Komentář, načítá se pouze z XML deklarace (ne z databáze, tam není).
        /// </summary>
        public string Comment { get; protected set; }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            string text = this.GetType().Name + ": Id=" + this.Id.ToString() + "; Name=" + this.Name;
            if (!String.IsNullOrEmpty(this.Comment))
                text += "  (" + this.Comment + ")";
            return text;
        }
        /// <summary>
        /// Potomek musí načíst data načtená z databáze do svých property.
        /// Jediná abstract metoda.
        /// </summary>
        /// <param name="data"></param>
        protected abstract void Fill(RecordFillInfo data);
        #endregion
        #region Konverze enumů, db hodnot, XML hodnot, db settingu
        /// <summary>
        /// Vrací true, pokud daný atribut je On, 1, Y, A, Yes, Ano. Jinak vrací false.
        /// Pokud atribut neexistuje, vrací (defaultValue).
        /// </summary>
        /// <param name="element"></param>
        /// <param name="attributeName"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        protected static bool XmlToProperty(XElement element, string attributeName, bool defaultValue)
        {
            string value = element.GetAttributeString(attributeName);
            if (value != null)
            {
                return (value == "On" || value == "1" || value == "Y" || value == "A" || value == "Yes" || value == "Ano");
            }
            return defaultValue;
        }
        /// <summary>
        /// Vrací ON pro value = true, jinak vrací OFF.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        protected static string PropertyToSqlCommand(bool value)
        {
            return (value ? "ON" : "OFF");
        }
        #endregion
        #region IRecordFill Members
        void IRecordFill.Fill(RecordFillInfo data)
        {
            if (data.ContainColumn("id"))
                this.Id = (int)data.Get("id");
            if (data.ContainColumn("name"))
                this.Name = (string)data.Get("name");

            this.Fill(data);
        }
        #endregion
        #region On-demand donačítání sloupců a indexů
        /// <summary>
        /// Zajistí načtení dalšího obsahu tohoto objektu (sloupce, indexy, texty, práva, atd).
        /// Tato varianta využívá standardní connection = Creator.Database.GetConnection(), 
        /// a načítá data, jen pokud ještě načtena nebyla (force = false).
        /// </summary>
        public void LoadContent()
        { this.LoadContent(Stator.Database.GetConnection(), false); }
        /// <summary>
        /// Zajistí načtení dalšího obsahu tohoto objektu (sloupce, indexy, texty, práva, atd).
        /// Tato varianta využívá standardní connection = Creator.Database.GetConnection().
        /// Dovoluje načíst data povinně = i poté, kdy už byla načtena (lze zadat force = true).
        /// </summary>
        /// <param name="force">Načíst povinně, i když už jsou data načtena</param>
        public void LoadContent(bool force)
        { this.LoadContent(Stator.Database.GetConnection(), force); }
        /// <summary>
        /// Zajistí načtení dalšího obsahu tohoto objektu (sloupce, indexy, texty, práva, atd).
        /// Tato varianta využívá explicitně dodanou connection.
        /// Data načítá, jen pokud ještě načtena nebyla (force = false).
        /// </summary>
        /// <param name="connect">Explicitně dodaná connection</param>
        public void LoadContent(DatabaseConnect connect)
        { this.LoadContent(connect, false); }
        /// <summary>
        /// Zajistí načtení dalšího obsahu tohoto objektu (sloupce, indexy, texty, práva, atd).
        /// Tato varianta využívá explicitně dodanou connection.
        /// Dovoluje načíst data povinně = i poté, kdy už byla načtena (lze zadat force = true).
        /// Tato varianta je určená k přepsání, je to vlastní výkonná metoda.
        /// </summary>
        /// <param name="connect">Explicitně dodaná connection</param>
        /// <param name="force">Načíst povinně, i když už jsou data načtena</param>
        public virtual void LoadContent(DatabaseConnect connect, bool force)
        { }
        #endregion
        #region Patchování
        /// <summary>
        /// Zajistí, že konkrétní objekt (odpovídající this) bude mít v databázi shodný stav, jaký má objekt this.
        /// Typicky: this je načteno z deklarace (XML definice obrazu databáze), 
        /// pro svou potřebu si skrz connaction načte reálný obraz objektu, porovná rozdíly a na rozdíly vytvoří skript a spustí jej.
        /// Skript zajistí shodu vlastností.
        /// Tato verze NENÍ určena k přepsání na potomku: 
        /// získává DatabaseConnect z Creator.Database.GetConnection()
        /// a poté volá přetížení s parametrem connect.
        /// </summary>
        public void Patch()
        { this.Patch(Stator.Database.GetConnection()); }
        /// <summary>
        /// Zajistí, že konkrétní objekt (odpovídající this) bude mít v databázi shodný stav, jaký má objekt this.
        /// Typicky: this je načteno z deklarace (XML definice obrazu databáze), 
        /// pro svou potřebu si skrz connaction načte reálný obraz objektu, porovná rozdíly a na rozdíly vytvoří skript a spustí jej.
        /// Skript zajistí shodu vlastností.
        /// Tato verze je určena k přepsání na potomku.
        /// </summary>
        /// <param name="connect"></param>
        public virtual void Patch(DatabaseConnect connect)
        { }
        /// <summary>
        /// Dané texty opatří hrnatými závorkami a oddělí tečkami: pro vstup: "bio", "tabulka" vrátí text "[bio].[tabulka]"
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        protected string Quotted(params string[] items)
        {
            string result = "";
            foreach (string item in items)
                result += (item == null ? "" : ".[" + item + "]");
            return (result.Length == 0 ? "" : result.Substring(1));
        }
        #endregion
        #region Static služby, konstanty
        /// <summary>
        /// Spojí a vrátí dané návy do jednoho, oddělí je tečkou.
        /// Tedy: z parametrů "database", "schema", "table" vrátí text "database.schema.table"
        /// </summary>
        /// <param name="names"></param>
        /// <returns></returns>
        public static string JoinObjectNames(params string[] names)
        {
            string result = "";
            foreach (string name in names)
            {
                if (name != null)
                    result += "." + name.Trim();
            }
            return (result.Length == 0 ? "" : result.Substring(1));
        }
        /// <summary>
        /// Token, který je v SqlSelectech umístěn v místě fráze WHERE. Konkrétní 
        /// </summary>
        public const string TOKEN_WHERE = "{{Where}}";
        #endregion
    }
    #endregion
}
